#include <utility>

#include "Button.h"
#include "Scene.h"
#include "glslshader.h"
#include "TextureManager.h"
#include "FreeTypeFont.h"
#include "FontManager.h"
#include "GLTexture.h"

using namespace std;

GLSLProgram* Button::m_shaderProgram = NULL;

Button::Button(boost::shared_ptr<Scene> scene, const ButtonState state)
    : m_scene(scene), m_position(), m_pixelsSize(), m_fontSize(18), m_clicked(false),
    m_labelLength(0.0f), m_buttonState(state)
{
    m_idleTexturePosition = Vector3(227.0f, 509.0f, 0.0f);
    m_activeTexturePosition = Vector3(280.0f, 509.0f, 0.0f);
    m_sizeInTexture = Dimension(50.0f, 50.0f);
}

Button::~Button(void)
{
}

bool Button::initialize(void)
{
    TextureManager* manager = TextureManager::getInstance();
    if (manager->hasTexture("data/textures/levelselection1.tga"))
    {
        m_texture = manager->getTexture("data/textures/levelselection1.tga");
    }
    else
    {
        m_texture = boost::shared_ptr<GLTexture>(new GLTexture("data/textures/levelselection1.tga", GL_LINEAR, 
                                  GL_LINEAR_MIPMAP_LINEAR, GL_RGB8, GL_RGB,
                                  GL_UNSIGNED_BYTE, true));
        manager->addTexture(m_texture);
    }

    if (m_buttonState == IDLE)
    {
        m_positionInTexture = m_idleTexturePosition;
    }
    else if (m_buttonState == ACTIVE)
    {
        m_positionInTexture = m_activeTexturePosition;
    }    

    GLfloat vertex [] = {  0.0f,  0.0f, //First Square vertex
                           m_pixelsSize.width,  0.0f,
                           m_pixelsSize.width, m_pixelsSize.height,
                           0.0f, m_pixelsSize.height  //Last Square vertex
    };

    glGenBuffers(1, &m_vertexBuffer); //Generate a buffer for the vertices
    glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); //Bind the vertex buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, &vertex[0], GL_STATIC_DRAW); //Send the data to OpenGL

    GLfloat texCoords [] = { 
        m_positionInTexture.x / m_texture->width(), m_positionInTexture.y / m_texture->height(), 
        (m_positionInTexture.x + m_sizeInTexture.width) / m_texture->width(), m_positionInTexture.y / m_texture->height(), 
        (m_positionInTexture.x + m_sizeInTexture.width) / m_texture->width(), (m_positionInTexture.y + m_sizeInTexture.height) / m_texture->height(), 
        m_positionInTexture.x / m_texture->width(), (m_positionInTexture.y + m_sizeInTexture.height) / m_texture->height()
    };

    glGenBuffers(1, &m_texCoordBuffer); //Generate a buffer for the vertices
    glBindBuffer(GL_ARRAY_BUFFER, m_texCoordBuffer); //Bind the vertex buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, &texCoords[0], GL_STATIC_DRAW); //Send the data to OpenGL

    if (m_shaderProgram == NULL)
    {
        m_shaderProgram = new GLSLProgram("data/shaders/simple-texture.vert", 
                                            "data/shaders/simple-texture.frag");

        if (!m_shaderProgram->initialize())
        {
            std::cerr << "Could not initialize the explosion shaders" << std::endl;
            return false;
        }

        m_shaderProgram->bindAttrib(0, "a_Vertex");
        m_shaderProgram->bindAttrib(1, "a_TexCoord0");
        m_shaderProgram->linkProgram();
    }

    // Initialize font
    FontManager* fontManager = FontManager::getInstance();
    if (fontManager->hasFont(make_pair("data/textures/ANGRY_BIRDS.ttf", m_fontSize)))
    {
        m_font = fontManager->getFont(make_pair("data/textures/ANGRY_BIRDS.ttf", m_fontSize));
    }
    else
    {
        GLint viewport[4];
        glGetIntegerv(GL_VIEWPORT, viewport);

        m_font = boost::shared_ptr<FreeTypeFont>(new FreeTypeFont("data/textures/ANGRY_BIRDS.ttf",
                                  viewport[2], 
                                  viewport[3], 
                                  m_fontSize));
        fontManager->addFont(m_font);
    }

    return true;
}

void Button::prepare(float dt)
{
    if (m_scene->isButtonPressed(0))
    {
        int mousePositionX;
        int mousePositionY;
        m_scene->getMousePos(mousePositionX, mousePositionY);

        // Check if left-mouse button has been clicked over the entity
        int left = static_cast<int> (m_position.x - (m_pixelsSize.width / 2.0f));
        if ((mousePositionX >= left) && (mousePositionX <= (left + m_pixelsSize.width)))
        {
            int top = static_cast<int> (m_position.y + (m_pixelsSize.height / 2.0f));
            if ((mousePositionY >= (top - m_pixelsSize.height)) && (mousePositionY <= top))
            {
                m_clicked = true;
            }
        }
    } // if (m_scene->isButtonPressed(0))
}

void Button::render(void)
{
    // Render button background
    static float modelviewMatrix[16];
    static float projectionMatrix[16];

    glDisable(GL_CULL_FACE);
    glDisable(GL_DEPTH_TEST);

    glPushMatrix();
    glTranslatef(m_position.x - (m_pixelsSize.width / 2.0f), 
                 m_position.y - (m_pixelsSize.height / 2.0f), 
                 m_position.z);

    glGetFloatv(GL_MODELVIEW_MATRIX, modelviewMatrix);
    glGetFloatv(GL_PROJECTION_MATRIX, projectionMatrix);

    m_shaderProgram->bindShader();
    m_shaderProgram->sendUniform4x4("modelview_matrix", modelviewMatrix);
    m_shaderProgram->sendUniform4x4("projection_matrix", projectionMatrix);
    m_shaderProgram->sendUniform("texture0", 0);

    glBindTexture(GL_TEXTURE_2D, m_texture->textureID());

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
    glVertexAttribPointer((GLint)0, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, m_texCoordBuffer);
    glVertexAttribPointer((GLint)1, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glDrawArrays(GL_QUADS, 0, 4);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);

    glPopMatrix();

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

    // Render label
    m_font->printString(m_label, 
                        m_position.x - (m_labelLength / 2), 
                        m_position.y - (m_fontSize / 2));
}

void Button::setLabel(const std::string& label)
{
    m_label = label;
    m_labelLength = m_font->textLength(m_label);
}

void Button::setButtonState(const ButtonState buttonState) 
{ 
    if (buttonState != m_buttonState)
    {
        m_buttonState = buttonState; 
        if (m_buttonState == IDLE)
        {
            m_positionInTexture = m_idleTexturePosition;
        }
        else if (m_buttonState == ACTIVE)
        {
            m_positionInTexture = m_activeTexturePosition;
        }

        glBindBuffer(GL_ARRAY_BUFFER, m_texCoordBuffer);
        GLfloat texCoords [] = { 
            m_positionInTexture.x / m_texture->width(), m_positionInTexture.y / m_texture->height(), 
            (m_positionInTexture.x + m_sizeInTexture.width) / m_texture->width(), m_positionInTexture.y / m_texture->height(), 
            (m_positionInTexture.x + m_sizeInTexture.width) / m_texture->width(), (m_positionInTexture.y + m_sizeInTexture.height) / m_texture->height(), 
            m_positionInTexture.x / m_texture->width(), (m_positionInTexture.y + m_sizeInTexture.height) / m_texture->height()
        };
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * 8, &texCoords[0]);
    }
}
